package com.czmh.etc.onlinepubsys.utils;

import com.czmh.etc.onlinepubsys.enums.cmg.ApiCodeEnum;
import com.czmh.etc.onlinepubsys.exceptions.ApiCodeException;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: RSA加解密工具类
 */
public class RSAUtils {

    /**
     * 算法标识
     */
    private static final String ALGORITHM = "RSA";
    /**
     * 密钥长度
     */
    private static final int KEY_SIZE = 1024;
    /**
     * 编码格式
     */
    private static final String CHARSET_NAME = "UTF-8";

    /**
     * 最大加密字节数117，超出最大字节数需要分组加密
     */
    private static int MAX_ENCRYPT_BLOCK = 117;

    /**
     * 最大解密字节数128，超出最大字节数需要分组加密
     */
    private static int MAX_DECRYPT_BLOCK = 128;

    /**
     * 随机生成密钥对
     *
     * @throws Exception
     */
    public static Map<Integer, String> generateKeyPair() throws Exception {
        Map<Integer, String> keyPairMap = new HashMap<Integer, String>();
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
        String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
        keyPairMap.put(0, publicKeyString);
        keyPairMap.put(1, privateKeyString);
        return keyPairMap;
    }

    /**
     * RSA公钥加密
     *
     * @param str       待加密字符串
     * @param publicKey 公钥
     * @return 密文字符串
     * @throws Exception 加密过程中出现的异常
     */
    public static String encrypt(String str, String publicKey) {
        try {
            // base64编码的公钥
            byte[] decoded = Base64.decodeBase64(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGORITHM)
                    .generatePublic(new X509EncodedKeySpec(decoded));
            // RSA加密
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            String outStr = null;
            byte[] inputArray = str.getBytes(CHARSET_NAME);
            int inputLength = inputArray.length;
            // 最大加密字节数，超出最大字节数需要分组加密
            int offSet = 0; // 标识
            byte[] resultBytes = {};
            byte[] cache = {};
            while (inputLength - offSet > 0) {
                if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
                    offSet += MAX_ENCRYPT_BLOCK;
                } else {
                    cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                    offSet = inputLength;
                }
                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
                System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
            }
            outStr = Base64.encodeBase64String(resultBytes);
            return outStr;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ApiCodeException(ApiCodeEnum.ERR_ENCRYPTION_FAILED);
        }
    }

    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 明文字符串
     * @throws Exception 解密过程中出现的异常
     */
    public static String decrypt(String str, String privateKey) {
        try {
            byte[] decoded = Base64.decodeBase64(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGORITHM)
                    .generatePrivate(new PKCS8EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            String outStr = null;
            byte[] inputArray = Base64.decodeBase64(str.getBytes(CHARSET_NAME));
            int inputLength = inputArray.length;
            // 最大加密字节数，超出最大字节数需要分组加密
            int offSet = 0; // 标识
            byte[] resultBytes = {};
            byte[] cache = {};
            while (inputLength - offSet > 0) {
                if (inputLength - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(inputArray, offSet, MAX_DECRYPT_BLOCK);
                    offSet += MAX_DECRYPT_BLOCK;
                } else {
                    cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                    offSet = inputLength;
                }
                resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
                System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
            }
            outStr = new String(resultBytes, CHARSET_NAME);
            return outStr;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ApiCodeException(ApiCodeEnum.ERR_DECRYPT_FAILED);
        }
    }

    public static void main(String[] args) throws Exception {
		String pub = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8BGwA9DJhfHU9w4biGLkMT5RZ+EiDL7nopCSPoWx94t1e+culCmw+fXpJ04xB9VxFz+FVEJdqyhN+kEzbHeQx8FFFX4fCpNJrlgyRfkxv1bThBgvZ6ndBeYjyk+28rX4m0LaxBY8PI66OIjSvm6r7Ar/B2s/2gB/FY8ZXRHxYrwIDAQAB";
		String pri = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALwEbAD0MmF8dT3DhuIYuQxPlFn4SIMvueikJI+hbH3i3V75y6UKbD59eknTjEH1XEXP4VUQl2rKE36QTNsd5DHwUUVfh8Kk0muWDJF+TG/VtOEGC9nqd0F5iPKT7bytfibQtrEFjw8jro4iNK+bqvsCv8Haz/aAH8VjxldEfFivAgMBAAECgYAVOJaMIQPStv5wozIFMO8p8Y13/isJC4j7NhOeVoTtTH7Z6m3Oyfdfsj/sN0FcbHomswjm1gGRHw/GODlEfgI0YzkjxY6mO1jGX3oM2xKhlGE40bvWcn895RYTNjUYPklkBh9rKt6gQ9bAherjt0Mju5cVW19I8q7SKyn8d7AR2QJBAN4RER8YUWAtKIKyAXySjk8DzDTAaotLyvBXNik5FgyYwyjiexZVV9nhsZTg26sTYAIO/HR2Vyf1dKSXs7b7640CQQDYv2QXX/rNjppia3mBwttL1E5WsCyrWqfXg7+FlgG0iCSUta0DRV/Zzl8tK6AXQUqveG4Z4J6eZOm3qrBehOgrAkAf8+i4xrD+28CWfbKFEDl12SAK/xzVikRbLNFYQSSRD3IVcfEwF3uamEULHQe+p/0Kjsl4oIaN812jSUjMrZcJAkAX7LhdpCiI/A6qgnZ9DkcqjZrAEt47Y9xW+6HuWuqBRO3UxGD+lW/JGPCEieY36usVh1OP461eioEJ1vt003LVAkEAq06+dqDeZYL9Ge4Fcjf/NghOoN94S4IPhJg2IpFL41w1QSYR+GhuEqGTnkJBET7xAvCyM6pK4Au/Z4AeGncwsA==";
        String s = RSAUtils.encrypt("{\n" +
                "    \"vehiclePlate\":\"京A00000\",\"vehiclePlateColor\":0,\"vehicleType\":1\n" +
                "}", pub);
		System.out.println(s);
		String ss = RSAUtils.decrypt(s,pri);
		System.out.println(ss);
    }


}
